#include "plotcurve.h"
#include "qwt/qwt_curve_fitter.h"
#include "qwt/qwt_painter.h"
#include "qwt/qwt_point_mapper.h"
#include "qwt/qwt_clipper.h"

MyQwtPlotCurve::MyQwtPlotCurve(const QString &title)
    : QwtPlotCurve(title)
{
    MyInit();
}

MyQwtPlotCurve::MyQwtPlotCurve(const QwtText &title)
    : QwtPlotCurve(title)
{
    MyInit();
}

MyQwtPlotCurve::~MyQwtPlotCurve()
{

}

void MyQwtPlotCurve::MyInit()
{
    m_CurveInterval = new QVector<CurveInterval>();
    XMove = 0.0;
    YMove = 0.0;
}

/*!
  Draw an interval of the curve

  \param painter Painter
  \param xMap Maps x-values into pixel coordinates.
  \param yMap Maps y-values into pixel coordinates.
  \param canvasRect Contents rect of the canvas
  \param from Index of the first point to be painted
  \param to Index of the last point to be painted. If to < 0 the
         curve will be painted to its last point.

  \sa drawCurve(), drawSymbols(),
*/
void MyQwtPlotCurve::drawSeries( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
{
    const size_t numSamples = dataSize();

    if ( !painter || numSamples <= 0 )
        return;

    if ( to < 0 )
        to = numSamples - 1;

    if ( qwtVerifyRange( numSamples, from, to ) > 0 )
    {
//        painter->save();
//        painter->setPen( this->pen() );


        if(!m_CurveInterval->isEmpty())
        {
            for(int i=0; i<m_CurveInterval->size(); i++)
            {
                int newFrom = m_CurveInterval->value(i).getFrom();
                int newTo = m_CurveInterval->value(i).getTo();
                if(qwtVerifyRange(dataSize(),newFrom,newTo)>0)
                {
                    painter->save();
                    painter->setPen( this->pen() );//curve pen

                    drawCurve( painter, this->style(), xMap, yMap, canvasRect, newFrom, newTo );
                    painter->restore();

                    if ( this->symbol() &&
                        ( this->symbol()->style() != QwtSymbol::NoSymbol ) )
                    {
                        painter->save();
                        drawSymbols( painter, *this->symbol(),
                            xMap, yMap, canvasRect, newFrom, newTo );
                        painter->restore();
                    }
                }
            }
        }else{
            painter->save();
            painter->setPen( this->pen() );

            drawCurve( painter, this->style(), xMap, yMap, canvasRect, from, to );
            painter->restore();

            if ( this->symbol() &&
                ( this->symbol()->style() != QwtSymbol::NoSymbol ) )
            {
                painter->save();
                drawSymbols( painter, *this->symbol(),
                    xMap, yMap, canvasRect, from, to );
                painter->restore();
            }
        }

//        //test
//        if(to>50)
//            to = 50;

        /*
          Qt 4.0.0 is slow when drawing lines, but it's even
          slower when the painter has a brush. So we don't
          set the brush before we really need it.
         */

//        drawCurve( painter, this->style(), xMap, yMap, canvasRect, from, to );
//        painter->restore();

//        if ( this->symbol() &&
//            ( this->symbol()->style() != QwtSymbol::NoSymbol ) )
//        {
//            painter->save();
//            drawSymbols( painter, *this->symbol(),
//                xMap, yMap, canvasRect, from, to );
//            painter->restore();
//        }
    }
}

/*!
  \brief Draw the line part (without symbols) of a curve interval.
  \param painter Painter
  \param style curve style, see QwtPlotCurve::CurveStyle
  \param xMap x map
  \param yMap y map
  \param canvasRect Contents rect of the canvas
  \param from index of the first point to be painted
  \param to index of the last point to be painted
  \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks()
*/
void MyQwtPlotCurve::drawCurve( QPainter *painter, int style,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
{
    switch ( style )
    {
        case Lines:
            if ( testCurveAttribute( Fitted ) )
            {
                // we always need the complete
                // curve for fitting
                // if want Interval draw neet deal with other
                from = 0;
                to = dataSize() - 1;
            }
            drawLines( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Sticks:
            drawSticks( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Steps:
            drawSteps( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Dots:
            drawDots( painter, xMap, yMap, canvasRect, from, to );
            break;
        case UserCurve:
            if ( testCurveAttribute( Fitted ) )
            {
                // we always need the complete
                // curve for fitting
                // if want Interval draw neet deal with other
                from = 0;
                to = dataSize() - 1;

//                //test
//                if(to>50)
//                    to = 50;
            }
//            if(!m_CurveInterval->isEmpty())
//            {
//                for(int i=0; i<m_CurveInterval->size(); i++)
//                {
//                    int newFrom = m_CurveInterval->value(i).getFrom();
//                    int newTo = m_CurveInterval->value(i).getTo();
//                    if(qwtVerifyRange(dataSize(),newFrom,newTo)>0)
//                    {
//                        drawMyLines( painter, xMap, yMap, canvasRect, newFrom, newTo );
//                    }
//                }
//            }else{
//                drawMyLines( painter, xMap, yMap, canvasRect, from, to );
//            }
            drawMyLines( painter, xMap, yMap, canvasRect, from, to );
            break;
        case NoCurve:
        default:
            break;
    }
}


/*!
  \brief Draw lines

  If the CurveAttribute Fitted is enabled a QwtCurveFitter tries
  to interpolate/smooth the curve, before it is painted.

  \param painter Painter
  \param xMap x map
  \param yMap y map
  \param canvasRect Contents rect of the canvas
  \param from index of the first point to be painted
  \param to index of the last point to be painted

  \sa setCurveAttribute(), setCurveFitter(), draw(),
      drawLines(), drawDots(), drawSteps(), drawSticks()
*/
void MyQwtPlotCurve::drawMyLines( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
{
    if ( from > to )
        return;

    const bool doAlign = QwtPainter::roundingAlignment( painter );
    const bool doFit = ( testCurveAttribute(Fitted)/*d_data->attributes& Fitted*/  ) && this->curveFitter();
    const bool doFill = ( this->brush().style() != Qt::NoBrush )
            && ( this->brush().color().alpha() > 0 );

    QRectF clipRect;
    if ( testPaintAttribute(ClipPolygons)/*d_data->paintAttributes & ClipPolygons*/ )
    {
        qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
        clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
    }

    // The raster paint engine is significantly faster
    // for rendering QPolygon than for QPolygonF. So let's
    // see if we can use them.

    bool doIntegers = false;

    if ( doAlign && !testRenderHint( QwtPlotItem::RenderAntialiased )
        && !QwtPainter::isX11GraphicsSystem() )
    {
        // In case of filling or fitting performance doesn't count
        // because both operations are much more expensive
        // then drawing the polyline itsself

        if ( !doFit && !doFill )
            doIntegers = true;
    }

    const bool noDuplicates = testPaintAttribute(FilterPoints)/*d_data->paintAttributes & FilterPoints*/;

    QwtPointMapper mapper;
    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
    mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
    mapper.setBoundingRect( canvasRect );

    if ( doIntegers )
    {
        /*const*/ QPolygon polyline = mapper.toPolygon(
            xMap, yMap, data(), from, to );
        polyline = polyline.translated(QPoint(int(XMove),int(YMove)));
        if (testPaintAttribute(ClipPolygons)/* d_data->paintAttributes & ClipPolygons */)
        {
            const QPolygon clipped = QwtClipper::clipPolygon(
                clipRect.toAlignedRect(), polyline, false );

            QwtPainter::drawPolyline( painter, clipped );
        }
        else
        {
            QwtPainter::drawPolyline( painter, polyline );
        }
    }
    else
    {
        QPolygonF polyline = mapper.toPolygonF( xMap, yMap,
            data(), from, to );
        polyline = polyline.translated(XMove,YMove);

        if ( doFit )
            polyline = this->curveFitter()->fitCurve( polyline );

        if ( testPaintAttribute(ClipPolygons)/*d_data->paintAttributes & ClipPolygons*/ )
        {
            const QPolygonF clipped = QwtClipper::clipPolygonF(
                clipRect, polyline, false );

            QwtPainter::drawPolyline( painter, clipped );
        }
        else
        {
            QwtPainter::drawPolyline( painter, polyline );
        }

        if ( doFill )
        {
            fillCurve( painter, xMap, yMap, canvasRect, polyline );
        }
    }
}


/*!
  Draw symbols

  \param painter Painter
  \param symbol Curve symbol
  \param xMap x map
  \param yMap y map
  \param canvasRect Contents rect of the canvas
  \param from Index of the first point to be painted
  \param to Index of the last point to be painted

  \sa setSymbol(), drawSeries(), drawCurve()
*/
void MyQwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
{      
#ifndef Q_OS_WIN32
    const bool doAlign = QwtPainter::roundingAlignment( painter );

    bool usePixmap = testPaintAttribute( QwtPlotCurve::ClipPolygons );
    if ( usePixmap && !doAlign )
    {
        // Don't use the pixmap, when the paint device
        // could generate scalable vectors

        usePixmap = false;
    }

    if ( usePixmap )
    {
        QPixmap pm( symbol.boundingRect().size() );
        pm.fill( Qt::transparent );

        const double pw2 = 0.5 * pm.width();
        const double ph2 = 0.5 * pm.height();

        QPainter p( &pm );
        p.setRenderHints( painter->renderHints() );
        symbol.drawSymbol( &p, QPointF( pw2, ph2 ) );
        p.end();

        const QwtSeriesData<QPointF> *series = data();

        for ( int i = from; i <= to; i++ )
        {
            const QPointF sample = series->sample( i )+QPointF(XMove,YMove);

            double xi = xMap.transform( sample.x() );
            double yi = yMap.transform( sample.y() );
            if ( doAlign )
            {
                xi = qRound( xi );
                yi = qRound( yi );
            }

            if ( canvasRect.contains( xi, yi ) )
            {
                const int left = qCeil( xi - pw2 );
                const int top = qCeil( yi - ph2 );

                painter->drawPixmap( left, top, pm );
            }
        }
    }
    else
    {
        QwtPointMapper mapper;
        mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
        mapper.setFlag( QwtPointMapper::WeedOutPoints,
            testPaintAttribute( QwtPlotCurve::FilterPoints ) );
        mapper.setBoundingRect( canvasRect );

        const int chunkSize = 500;

        for ( int i = from; i <= to; i += chunkSize )
        {
            const int n = qMin( chunkSize, to - i + 1 );

            const QPolygonF points = mapper.toPointsF( xMap, yMap,
                data(), i, i + n - 1 ).translated(XMove,YMove);

            if ( points.size() > 0 )
                symbol.drawSymbols( painter, points );
        }
    }
#else
    QwtPointMapper mapper;
    mapper.setFlag( QwtPointMapper::RoundPoints,
        QwtPainter::roundingAlignment( painter ) );
    mapper.setFlag( QwtPointMapper::WeedOutPoints,
        testPaintAttribute( QwtPlotCurve::FilterPoints ) );
    mapper.setBoundingRect( canvasRect );

    const int chunkSize = 500;

    for ( int i = from; i <= to; i += chunkSize )
    {
        const int n = qMin( chunkSize, to - i + 1 );

        const QPolygonF points = mapper.toPointsF( xMap, yMap,
            data(), i, i + n - 1 ).translated(XMove,YMove);

        if ( points.size() > 0 )
            symbol.drawSymbols( painter, points );
    }
#endif


}

void MyQwtPlotCurve::setCurveInterval( QVector<CurveInterval> &rValue)
{
    m_CurveInterval = &rValue;
}

void MyQwtPlotCurve::setCurveMove( const double x_Move, const double y_Move)
{
    XMove = x_Move;
    YMove = y_Move;
}

void MyQwtPlotCurve::dataChanged()
{
	QwtPlotCurve::dataChanged();
}

size_t MyQwtPlotCurve::dataSize() const
{
	return QwtPlotCurve::dataSize();
}
QRectF MyQwtPlotCurve::dataRect() const
{
	return QwtPlotCurve::dataRect();
}
void MyQwtPlotCurve::setRectOfInterest( const QRectF &rect )
{
	QwtPlotCurve::setRectOfInterest(rect);
}
